package core.helpers;

import java.awt.Dimension;
import java.awt.Toolkit;
import java.io.File;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.apache.commons.io.FileUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.Point;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.Select;
import org.openqa.selenium.support.ui.Wait;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.TestException;

import core.Config;
import core.Pilot;

public class DriverHelper
{
	protected static Pilot pilot;
	public static WebDriver driver;
	public static WebDriver driverPrimary;
	public static WebDriver driverSecondary;
	private final boolean proxy = false;

	private final static int flashCount = 1;
	private final static int flashInterval = 500; // in milliseconds
	private final static String flashColor = "#FF6666";

	public DriverHelper(Pilot p_pilot)
	{
		pilot = p_pilot;
	}

	public WebDriver getDriver()
	{
		return driver;
	}

	public void setDriver(WebDriver p_webDriver)
	{
		pilot.log().debug("Set WebDriver");
		DriverHelper.driver = p_webDriver;
	}

	public void stop()
	{
		try
		{
			pilot.log().debug("Stop WebDriver");
			getDriver().quit();
		}
		catch (NullPointerException p_exception)
		{
			pilot.log().warn("Cannot stop WebDriver because it was not running\nError: " + p_exception);
		}
		catch (Exception p_exception)
		{
			pilot.log().warn("Cannot stop a WebDriver\nError:" + p_exception);
		}

	}

	public void close()
	{
		try
		{
			pilot.log().debug("Close => Browser");
			getDriver().close();

		}
		catch (Exception p_exception)
		{
			pilot.log().warn("Cannot close browser\nError: " + p_exception);
		}

	}

	public String getSessionId()
	{
		try
		{
			String sessionId = (((RemoteWebDriver) getDriver()).getSessionId()).toString();
			pilot.log().debug("Get session Id => " + sessionId);

			return sessionId;
		}
		catch (Exception p_exception)
		{
			pilot.log().warn("Cannot get session Id\nError: " + p_exception);

			return null;
		}

	}

	public String getCurrentUrl()
	{
		try
		{
			String currentUrl = getDriver().getCurrentUrl();
			pilot.log().debug("Get current URL => " + currentUrl);

			return currentUrl;
		}
		catch (Exception p_exception)
		{
			throw new TestException("Cannot get current page\nError: " + p_exception);
		}

	}

	public boolean checkUrlContains(String p_substring)
	{
		if (getCurrentUrl().contains(p_substring))
		{
			pilot.log().debug("Check URL contains [" + p_substring + "] => " + true);

			return true;
		}
		else
		{
			pilot.log().debug("Check URL contains [" + p_substring + "] => " + false);

			return false;
		}
	}

	public boolean checkUrlEnds(String p_substring)
	{
		if (getCurrentUrl().endsWith(p_substring))
		{
			pilot.log().debug("Check for URL ends with [" + p_substring + "] => " + true);

			return true;
		}
		else
		{
			pilot.log().debug("Check for is URL ends with [" + p_substring + "] => " + false);

			return false;
		}
	}

	public void refreshPage()
	{
		pilot.log().debug("Refresh the page");
		getDriver().navigate().refresh();
	}

	public WebDriver switchToWindow(String p_windowHandler)
	{
		pilot.log().debug("Switch to window => " + p_windowHandler);

		return getDriver().switchTo().window(p_windowHandler);
	}

	public void switchToActiveElement()
	{
		pilot.log().debug("Switch to active element");
		getDriver().switchTo().activeElement();
	}

	public Set<String> getWindowHandles()
	{
		pilot.log().debug("Get window handles");
		return getDriver().getWindowHandles();
	}

	public String getWindowHandle()
	{
		pilot.log().debug("Get window handle");
		return getDriver().getWindowHandle();
	}

	public void takeScreenShot(String p_pathToFile)
	{
		try
		{

			pilot.log().debug("Take a screenshot: " + p_pathToFile);
			if (pilot.dashboard().getPlatform().equals(pilot.config().PLATFORM_MOBILE) &&
					pilot.dashboard().getBrowserName().equals(pilot.config().BROWSER_ANDROID))
			{
				Process p;
				p = Runtime.getRuntime().exec("adb -s " + pilot.dashboard().getAppiumUdid() + " shell screencap -p /sdcard/screen.png");
				p.waitFor();

				p = Runtime.getRuntime().exec("adb -s " + pilot.dashboard().getAppiumUdid() + " pull /sdcard/screen.png");
				p.waitFor();

				File screenshot = new File("screen.png");
				FileUtils.copyFile(screenshot, new File(p_pathToFile));

				p = Runtime.getRuntime().exec("adb -s " + pilot.dashboard().getAppiumUdid() + " shell rm /sdcard/screen.png");
				p.waitFor();
			}
			else
			{
				File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
				FileUtils.copyFile(screenshot, new File(p_pathToFile));
			}

		}
		catch (Exception p_exception)
		{
			// throw new TestException("Cannot take a screenshot: " + p_exception);
			pilot.log().warn("Unable to take a Screenshot! : " + p_exception);
		}
	}

	public void setCookie(String p_name, String p_value, String p_domain, String p_path, Date p_expireDate)
	{
		pilot.log().debug("Set cookie => name=" + p_name + "; value=" + p_value + "; domain=" + p_domain + "; path=" + p_path + "; date=" + p_expireDate);

		try
		{
			Cookie cookie = new Cookie(p_name, p_value, p_domain, p_path, p_expireDate);
			getDriver().manage().addCookie(cookie);
		}
		catch (Exception p_exception)
		{
			throw new TestException("Cannot set a cookie\nError: " + p_exception);
		}
	}

	@SuppressWarnings("deprecation")
	public void doBasicAuthentification()
	{
		pilot.log().debug("Do basic authentification");

		if (pilot.thread().timeoutOccurred)
		{
			while (!pilot.thread().completed)
			{
				try
				{
					Thread.sleep(200);
				}
				catch (InterruptedException p_exception)
				{
					throw new TestException("Cannot sleep tread: " + p_exception);
				}
			}
		}
		else
		{
			pilot.thread().interrupt();
		}

		pilot.thread().stop();
	}

	/**
	 * Types value in a text field
	 * @param p_locator Web element locator
	 * @param p_value Text value
	 * @throws Exception
	 */
	public void type(By p_locator, String p_value)
	{
		try
		{

			pilot.log().debug("Type a value => [" + p_value + "] to [" + p_locator + "]");
			WebElement webElement = getDriver().findElement(p_locator);
			doBeforeActions(webElement);
			webElement.sendKeys(p_value);
		}
		catch (Exception p_exception)
		{
			throw new TestException("Cannot type a value '" + p_value + "' to the text field => " + p_locator + "\n" + p_exception);
		}
	}

	/**
	 * Reads out Text value of a First Selected option in Select
	 * @param p_locator 'Select' WebElement locator
	 * @return Text from First Selected Option
	 */
	public String getSelectedOptionText(By p_locator)
	{
		Select select = new Select(getDriver().findElement(p_locator));
		return select.getFirstSelectedOption().getText();
	}

	/**
	 * Clears and types value in a text field
	 * @param p_locator Web element locator
	 * @param p_value Text value
	 */
	public void typeAndClear(By p_locator, String p_value)
	{
		try
		{
			getDriver().findElement(p_locator).clear();
			type(p_locator, p_value);
		}
		catch (Exception p_exception)
		{
			throw new TestException("Cannot type a value to the text field => " + p_locator + "\n" + p_exception);
		}
	}

	/**
	 * Clicks on a web element
	 * @param p_locator Web element locator
	 */
	public void click(By p_locator)
	{
		pilot.log().debug("Click on an element => [" + p_locator + "]");
		try
		{
			Wait<WebDriver> wait = new WebDriverWait(driver, pilot.config().WEBDRIVER_WAIT);
			wait.until(ExpectedConditions.presenceOfElementLocated(p_locator));
			WebElement webElement = getDriver().findElement(p_locator);
			doBeforeActions(webElement);
			webElement.click();
		}
		catch (Exception p_exception)
		{
			throw new TestException("Cannot click on an element => " + p_locator + "\nReason: " + p_exception);
		}
	}

	/**
	 * Switches to a frame
	 * @param p_locator Web element locator
	 */
	public void switchToFrame(By p_locator)
	{
		pilot.log().debug("Switch to a frame => [" + p_locator + "]");

		try
		{
			getDriver().switchTo().frame(getDriver().findElement(p_locator));
		}
		catch (Exception p_exception)
		{
			throw new TestException("Cannot switch to a frame => " + p_locator + "\n" + p_exception);
		}
	}

	/**
	 * Selects a value in a select by option text using JavaScript jQuery
	 * @param p_select jQuery element locator as String
	 * @param p_text Option text
	 * @deprecated
	 */
	@Deprecated
	public void selectByTextJQ(String p_select, String p_text)
	{
		try
		{
			pilot.log().debug("Select an option trough JQ => [" + p_select + "] by text [" + p_text + "]");
			pilot.js().execute("$(\"" + p_select + "\").find(\"option:contains('" + p_text + "')\").each(function(){"
					+ "if( $(this).text() == '" + p_text + "' ) {" + "$(this).attr(\"selected\",\"selected\");}" + "});");
		}
		catch (Exception p_exception)
		{
			throw new TestException("Cannot select an option => " + p_select + "\n" + p_exception);
		}
	}

	/**
	 * Selects a value in a select by option text
	 * @param p_text Option text
	 */
	public void selectByText(By p_locator, String p_text)
	{
		try
		{
			WebElement webElement = getDriver().findElement(p_locator);
			Select webElementSelect = new Select(webElement);

			if (!webElementSelect.getOptions().isEmpty())
			{
				if (p_text.isEmpty())
				{
					throw new TestException("Cannot select an option => " + p_locator + "\nText parameter is empty");
				}
				else
				{
					pilot.log().debug("Select an option => [" + p_locator + "] by text [" + p_text + "]");

					try
					{
						doBeforeActions(webElement);
						webElementSelect.selectByVisibleText(p_text);
					}
					catch (Exception p_exception)
					{
						throw new TestException("Cannot select an option by visible text => " + p_locator + "\n" + p_exception);
					}
				}
			}
		}
		catch (Exception p_exception)
		{
			throw new TestException("Cannot select element " + p_locator + " by text " + p_text + "=> \n" + p_exception);
		}

	}

	/**
	 * Selects a value in a select by option index
	 * @param p_index Option index
	 */
	public void selectByIndex(By p_locator, int p_index)
	{
		try
		{
			pilot.log().debug("Select an option => [" + p_locator + "] by text [" + p_index + "]");
			WebElement webElement = getDriver().findElement(p_locator);
			Select webElementSelect = new Select(webElement);
			doBeforeActions(webElement);
			webElementSelect.selectByIndex(p_index);
		}
		catch (Exception p_exception)
		{
			throw new TestException("Cannot select an option => " + p_locator + "\n" + p_exception);
		}
	}

	/**
	 * Selects a value in a select by option value
	 * @param p_locator Web element locator
	 * @param p_value Option value
	 */
	public void selectByValue(By p_locator, String p_value)
	{
		try
		{
			pilot.log().debug("Select an option => [" + p_locator + "] by text [" + p_value + "]");
			WebElement webElement = getDriver().findElement(p_locator);
			doBeforeActions(webElement);
			Select webElementSelect = new Select(webElement);
			webElementSelect.selectByValue(p_value);
		}
		catch (Exception p_exception)
		{
			throw new TestException("Cannot select an option => " + p_locator + "\n" + p_exception);
		}
	}

	public boolean isElementDisplayed(By p_locator)
	{
		try
		{
			WebElement webElement = getDriver().findElement(p_locator);
			flashWebElement(webElement);
			boolean isElementDisplayed = webElement.isDisplayed();
			pilot.log().debug("Is element displayed [" + p_locator + "] => " + isElementDisplayed);
			return isElementDisplayed;
		}
		catch (Exception p_exception)
		{
			return false;
		}
	}

	/**
	 * Opens an URL
	 * @param p_url Page URL
	 */
	public void openURL(String p_url)
	{
		updateSite(p_url);

		try
		{
			pilot.log().debug("Open URL => " + p_url);
			getDriver().get(p_url);
		}
		catch (Exception p_exception)
		{
			pilot.log().error("Open URL => " + p_url + "\n" + p_exception.toString());
		}

		if (proxy)
		{
			pilot.driver().doBasicAuthentification();
		}
	}

	/**
	 * Opens a page
	 * @param p_pageUrl Page URL for opening
	 */
	public void openPage(String p_pageUrl)
	{
		try
		{
			pilot.log().debug("openPage =>" + p_pageUrl);
			openURL(pilot.driver().getCurrentUrl() + p_pageUrl);
		}
		catch (Exception p_exception)
		{
			throw new TestException("openPage => " + p_exception);
		}
	}

	public void wait(int p_timeOut)
	{
		pilot.log().debug("Pause for => " + p_timeOut + " seconds");

		try
		{
			Thread.sleep(p_timeOut * 1000);
		}
		catch (InterruptedException p_exception)
		{
			throw new TestException("Cannot pause =>" + p_exception);
		}
	}

	public String getText(By p_locator)
	{
		try
		{
			WebElement webElement = getDriver().findElement(p_locator);
			doBeforeActions(webElement);
			return webElement.getText();
		}
		catch (Exception p_exception)
		{
			pilot.log().error("getText => " + p_locator.toString() + "\n" + p_exception.toString());
			throw new TestException("getText => " + p_locator.toString() + "\n" + p_exception.toString());
		}
	}

	public String getText(WebElement p_webElement)
	{
		try
		{
			doBeforeActions(p_webElement);
			return p_webElement.getText();
		}
		catch (Exception p_exception)
		{
			pilot.log().error("getText => " + p_webElement.toString() + "\n" + p_exception.toString());
			throw new TestException("getText => " + p_webElement.toString() + "\n" + p_exception.toString());
		}
	}

	/**
	 * Flashes a web element
	 * @param p_webElement Web element
	 */
	@SuppressWarnings("static-access")
	public void flashWebElement(WebElement p_webElement)
	{
		String bgcolor = p_webElement.getCssValue("backgroundColor");

		for (int i = 0; i < flashCount; i++)
		{
			setWebElementBackgroundColor(flashColor, p_webElement);
			wait(flashInterval / 1000);
			setWebElementBackgroundColor(bgcolor, p_webElement);
		}
	}

	/**
	 * Changes a color of web element
	 * @param p_color Web element color
	 * @param p_webElement Web element
	 */
	private void setWebElementBorderColor(String p_color, WebElement p_webElement)
	{
		pilot.js().execute("arguments[0].setAttribute('style', arguments[1]);", p_webElement, "border: 2px solid " + p_color);
	}

	/**
	 * Changes a color of web element
	 * @param p_color Web element color
	 * @param p_webElement Web element
	 */
	@SuppressWarnings("unused")
	private void setWebElementTextColor(String p_color, WebElement p_webElement)
	{
		pilot.js().execute("arguments[0].setAttribute('style', arguments[1]);", p_webElement, "color: " + p_color);
	}

	/**
	 * Changes a color of web element
	 * @param p_color Web element color
	 * @param p_webElement Web element
	 */
	private void setWebElementBackgroundColor(String p_color, WebElement p_webElement)
	{
		pilot.js().execute("arguments[0].setAttribute('style', arguments[1]);", p_webElement, "background: " + p_color);
	}

	/**
	 * Uploads a file
	 * @param p_locator Web element locator
	 * @param p_pathToFile Path to file for uploading
	 * @throws Exception
	 */
	public void uploadFile(By p_locator, String p_pathToFile) throws Exception
	{
		pilot.log().debug("Upload a file " + p_pathToFile + " from [" + p_locator + "]");

		File file = new File(p_pathToFile);
		type(p_locator, file.getAbsolutePath());
	}

	/**
	 * Checks ON an option
	 * @param p_locator Web element locator
	 */
	public void checkOn(By p_locator)
	{
		if (!isOptionCheckedOn(p_locator))
		{
			click(p_locator);
			pilot.log().debug("Check ON an option [" + p_locator + "]: " + false + " => " + true);
		}
		else
		{
			throw new TestException("Cannot check ON an option. It is checked ON");
		}
	}

	/**
	 * Checks OFF an option
	 * @param p_locator Web element locator
	 */
	public void checkOff(By p_locator)
	{
		if (isOptionCheckedOn(p_locator))
		{
			click(p_locator);
			pilot.log().debug("Check OFF an option [" + p_locator + "]: " + true + " => " + false);
		}
		else
		{
			throw new TestException("Cannot check OFF an option => " + p_locator + "\nIt is checked OFF");
		}
	}

	/**
	 * Checks an option
	 * @param p_locator Web element locator
	 */
	public void check(By p_locator)
	{
		pilot.log().debug("Check an option [" + p_locator + "]: " + isOptionCheckedOn(p_locator) + " => " + !isOptionCheckedOn(p_locator));
		click(p_locator);
	}

	/**
	 * Checks an option by expected checked state
	 * @param p_locator Web element locator
	 * @param p_exceptionpectedCheckedState Expected checked state
	 */
	public void check(By p_locator, boolean p_exceptionpectedCheckedState)
	{
		boolean isCheckedNow = isOptionCheckedOn(p_locator);

		if ((p_exceptionpectedCheckedState && !isCheckedNow) || (!p_exceptionpectedCheckedState && isCheckedNow))
		{
			check(p_locator);
		}
	}

	/**
	 * Checks for is option checked on
	 * @param p_locator Web element locator
	 * @return Option is checked, or is not
	 */
	public boolean isOptionCheckedOn(By p_locator)
	{
		if (this.getAttribute(p_locator, "checked") != null)
		{
			pilot.log().debug("Check for is option checked on: " + true);
			return true;
		}
		else
		{
			pilot.log().debug("Check for is option checked on: " + false);
			return false;
		}
	}

	/**
	 * Scrolls to web element if needed
	 * @param p_locator Web element locator
	 */
	public void scrollTo(By p_locator)
	{
		try
		{
			pilot.log().debug("Scroll to web element: " + p_locator);
			pilot.js().execute("arguments[0].scrollIntoView(true);", getDriver().findElement(p_locator));
		}
		catch (Exception p_exception)
		{
			throw new TestException("Cannot scroll to web element => " + p_locator + "\n" + p_exception);
		}
	}

	public void scrollToCenderOfView()
	{
		try
		{
			pilot.log().debug("Scrolls to center of the page");
			// Gets current screen size
			Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
			// Gets height of the screen
			int height = (int) screenSize.getHeight();

			// Scrolls at the bottom to the page
			pilot.js().execute("window.scrollBy(0, " + height / 2 + " )");
			pilot.driver().wait(1);

		}
		catch (Exception p_exception)
		{
			throw new TestException("Cannot scroll to ");
		}
	}

	/**
	 * Scrolls to web element without exact location specified
	 * @param p_locator By locator
	 * @param p_attemptsNumber Int number of attempts
	 * @param p_isExceptionOnAbsence Throws exception on element absence
	 */
	public void scrollToBy(By p_locator, Integer p_attemptsNumber, Boolean p_isExceptionOnAbsence)
	{
		try
		{
			pilot.log().debug("Scroll to web element: " + p_locator.toString());

			int loadCount = 0;

			while (loadCount < p_attemptsNumber)
			{
				if (!pilot.driver().isElementPresent(p_locator))
				{
					pilot.driver().scrollDown();
					loadCount++;
				}
				else
				{
					break;
				}
			}

			if (!pilot.driver().isElementPresent(p_locator) && p_isExceptionOnAbsence == true)
			{
				throw new TestException("Unable to find element " + p_locator.toString() + ", after " + p_attemptsNumber + " iterations");
			}

		}
		catch (Exception E)
		{
			throw new TestException("Cannot scroll to web element => " + p_locator + "\n" + E.toString());
		}
	}

	/**
	 * Scrolls to the bottom of the web page
	 */
	public void scrollDown()
	{
		try
		{
			pilot.log().debug("Scroll down page");
			// Gets current screen size
			Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
			// Gets height of the screen
			int height = (int) screenSize.getHeight();

			// Scrolls at the bottom to the page
			pilot.js().execute("window.scrollBy(0, " + height + " )");
			pilot.driver().wait(1);
		}
		catch (Exception p_exception)
		{
			throw new TestException("Cannot scroll down page");
		}
	}

	/**
	 * Scrolls to the very TOP of the web page
	 */
	public void scrollUp()
	{
		try
		{
			pilot.log().debug("Scroll UP page");

			// Scrolls to the TOP of the page
			pilot.js().execute("$(window).scrollTop(0);");
			pilot.driver().wait(1);
		}
		catch (Exception p_exception)
		{
			pilot.log().warn("Cannot scroll UP page");
		}
	}

	/**
	 * Deletes all browser cookies
	 */
	public void deleteAllCookies()
	{
		pilot.log().debug("Delete all browser cookies");

		try
		{
			if (pilot.driver().isBrowserOpen() != true)
			{
				throw new TestException("Cannot find opened browser\n");
			}

			getDriver().manage().deleteAllCookies();
		}
		catch (Exception p_exception)
		{
			throw new TestException("Cannot clear browser cookies\n" + p_exception);
		}
	}

	public boolean isElementPresent(By by)
	{
		try
		{
			getDriver().manage().timeouts().implicitlyWait(0, TimeUnit.SECONDS);
			getDriver().findElement(by);
			getDriver().manage().timeouts().implicitlyWait(Config.WEBDRIVER_WAIT, TimeUnit.SECONDS);
			return true;
		}
		catch (Exception e)
		{
			return false;
		}
	}

	/**
	 * Gets a page source
	 * @return Return page source
	 */
	public String getPageSource()
	{
		return pilot.driver().getDriver().getPageSource();
	}

	/**
	 * Gets web elements list
	 * @param p_webElementsBlock Block with web elements on a page
	 * @return Web elements list
	 */
	public List<WebElement> getWebElements(By p_webElementsBlock)
	{
		pilot.log().debug("Get web elements list => " + p_webElementsBlock);
		return pilot.driver().getDriver().findElements(p_webElementsBlock);
	}

	/**
	 * Gets web element
	 * @param p_locator Web element locator
	 * @return Web elements list
	 */
	public WebElement getWebElement(By p_locator)
	{
		pilot.log().debug("Get web element => " + p_locator);
		return pilot.driver().getDriver().findElement(p_locator);
	}

	/**
	 * Gets text from drop down list
	 * @return String text
	 */
	public String getTextElementInSelect(By p_locator)
	{
		try
		{
			String result = "";
			WebElement tmpDynamicElement = null;

			tmpDynamicElement = getWebElement(p_locator);
			Select select = new Select(tmpDynamicElement);
			result = select.getFirstSelectedOption().getText();

			return result;
		}
		catch (Exception p_exception)
		{
			throw new TestException(p_exception.toString());
		}
	}

	/**
	 * Gets image size by given locator
	 * @param p_locator
	 * @return result
	 */
	public Dimension getImageSize(By p_locator)
	{
		Dimension result = null;
		try
		{
			WebElement element = pilot.driver().getWebElement(p_locator);
			int imageHeight = element.getSize().getHeight();
			int imageWidth = element.getSize().getWidth();
			result = new Dimension(imageWidth, imageHeight);
		}
		catch (Exception E)
		{
			throw new TestException(E.toString());
		}
		return result;
	}

	/**
	 * Sets element text
	 * @param p_locator
	 * @param isClicked
	 * @param p_text
	 */
	public void setElementText(By p_locator, Boolean isClicked, String p_text)
	{
		try
		{
			WebElement element = getDriver().findElement(p_locator);

			if (isClicked)
			{
				element.click();
			}

			element.clear();
			element.sendKeys(p_text);
		}
		catch (Exception p_exception)
		{
			throw new TestException("Cannot setElementText: " + p_exception);
		}
	}

	/**
	 * Clicks on an element and changes HTML attribute 'target' value to "_self"
	 * @param p_locator Web element locator
	 */
	public void clickAndOpenInCurrentWindow(By p_locator)
	{
		try
		{
			JavascriptExecutor js = (JavascriptExecutor) driver;

			WebElement element = getDriver().findElement(p_locator);
			pilot.js().execute("arguments[0].setAttribute('target', '_self')", element);
			pilot.driver().wait(1);
			element.click();
		}
		catch (Exception p_exception)
		{
			throw new TestException(p_exception.toString());
		}
	}

	/**
	 * Gets an attribute value from a web element
	 * @param p_attribute Attribute name
	 * @return String attribute value
	 */
	public String getAttribute(By p_locator, String p_attribute)
	{
		String attributeValue = "";
		WebElement element = null;
		if (this.isElementPresent(p_locator))
		{
			try
			{
				element = getDriver().findElement(p_locator);
				doBeforeActions(element);

				if (p_attribute.equals("text"))
				{
					attributeValue = element.getText();
				}
				else
				{
					attributeValue = element.getAttribute(p_attribute);
				}

				pilot.log().debug("Get attribute [" + p_attribute + "] from [" + element + "] => " + attributeValue);
			}
			catch (Exception p_exception)
			{
				throw new TestException("Cannot get attribute '" + p_attribute + "' => " + element + "\n" + p_exception);
			}
		}
		return attributeValue;
	}

	public String getAttribute(WebElement p_webElement, String p_attribute)
	{
		String attributeValue = "";

		try
		{
			doBeforeActions(p_webElement);

			attributeValue = p_webElement.getAttribute(p_attribute);

			pilot.log().debug("Get attribute [" + p_attribute + "] from [" + p_webElement + "] => " + attributeValue);
		}
		catch (Exception p_exception)
		{
			throw new TestException("Cannot get attribute '" + p_attribute + "' => " + p_webElement + "\n" + p_exception);
		}

		return attributeValue;
	}

	/**
	 * Helper for switching driver
	 * @param p_browserHandle
	 */
	public void switchDriverTo(WebDriver p_driver, String p_browserHandle)
	{
		try
		{
			setDriver(p_driver);
			pilot.driver().getDriver().switchTo().window(p_browserHandle);
		}
		catch (Exception p_exception)
		{
			throw new TestException(p_exception.toString());
		}
	}

	/**
	 * Helper for set window size
	 * @param p_driver
	 * @param p_x
	 * @param p_y
	 * @param p_width
	 * @param p_heigth
	 */
	public void setWindowSize(Integer p_x, Integer p_y, Integer p_width, Integer p_heigth)
	{
		try
		{
			org.openqa.selenium.Dimension wdDimension = new org.openqa.selenium.Dimension(p_width, p_heigth);
			getDriver().manage().window().setSize(wdDimension);
			getDriver().manage().window().setPosition(new Point(p_x, p_y));

		}
		catch (Exception p_exception)
		{
			throw new TestException(p_exception.toString());
		}
	}

	public void waitForClickable(By locatorOfElement, String p_message)
	{
		try
		{
			Wait<WebDriver> wait = new WebDriverWait(driver, pilot.config().WEBDRIVER_WAIT);
			wait.until(ExpectedConditions.elementToBeClickable(locatorOfElement));
			flashWebElement(getDriver().findElement(locatorOfElement));
		}
		catch (Exception p_exception)
		{
			pilot.log().errorAndScreen(p_message + " " + p_exception, Pilot.TEST_SCREENSHOT_PATH);
		}
	}

	public void waitForInvisibility(By locatorOfElement)
	{
		this.waitForInvisibility(locatorOfElement, null, true);
	}

	public void waitForInvisibility(By locatorOfElement, String p_message)
	{
		this.waitForInvisibility(locatorOfElement, p_message, true);
	}

	public void waitForInvisibility(By locatorOfElement, String p_message, Boolean isThrowable)
	{
		try
		{
			Wait<WebDriver> wait = new WebDriverWait(driver, pilot.config().WEBDRIVER_WAIT);
			wait.until(ExpectedConditions.invisibilityOfElementLocated(locatorOfElement));
		}
		catch (Exception p_exception)
		{
			if (p_message == null)
			{
				p_message = "";
			}

			if (isThrowable)
			{
				throw new TestException(p_message + " " + p_exception);
			}
			else
			{
				pilot.log().errorAndScreen(p_message + " [" + locatorOfElement.toString() + "], " + p_exception, Pilot.TEST_SCREENSHOT_PATH);
			}
		}
	}

	public void waitForVisibility(By locatorOfElement)
	{
		this.waitForVisibility(locatorOfElement, null, true);
	}

	public void waitForVisibility(By locatorOfElement, String p_message)
	{
		this.waitForVisibility(locatorOfElement, p_message, true);
	}

	public void waitForVisibility(By locatorOfElement, String p_message, Boolean isTrowable)
	{
		try
		{
			Wait<WebDriver> wait = new WebDriverWait(driver, pilot.config().WEBDRIVER_WAIT);
			wait.until(ExpectedConditions.visibilityOfElementLocated(locatorOfElement));
		}
		catch (Exception p_exception)
		{
			if (p_message == null)
			{
				p_message = "";
			}

			if (isTrowable)
			{
				throw new TestException("Message : " + p_message + " | Error: " + p_exception);
			}
			else
			{
				pilot.log().warn("Message : " + p_message + " | Error: " + p_exception);
			}
		}
	}

	public void waitForPresence(By locatorOfElement)
	{
		this.waitForPresence(locatorOfElement, null, true);
	}

	public void waitForPresence(By locatorOfElement, String p_message)
	{
		this.waitForPresence(locatorOfElement, p_message, true);
	}

	public void waitForPresence(By locatorOfElement, String p_message, Boolean isTrowable)
	{
		try
		{
			Wait<WebDriver> wait = new WebDriverWait(driver, pilot.config().WEBDRIVER_WAIT);
			wait.until(ExpectedConditions.presenceOfElementLocated(locatorOfElement));
		}
		catch (Exception p_exception)
		{
			if (p_message == null)
			{
				p_message = "";
			}

			if (isTrowable)
			{
				throw new TestException(p_message + " | " + p_exception);
			}
			else
			{
				pilot.log().warn(p_message + " | " + p_exception);
			}
		}
	}

	/**
	 * Navigates forward to a page
	 */
	public void navigateForward()
	{
		try
		{
			pilot.log().debug("Navigate forward to a page");
			getDriver().navigate().forward();
		}
		catch (Exception p_exception)
		{
			pilot.log().warn("WCannot navigate back to a page");
		}

		wait(Config.WEBDRIVER_WAIT / 2);
		updateSite(getCurrentUrl());
	}

	/**
	 * Navigates back to a page
	 */
	public void navigateBack()
	{
		try
		{
			pilot.log().debug("Navigate back to a page");
			getDriver().navigate().back();
		}
		catch (Exception p_exception)
		{
			pilot.log().warn("WCannot navigate back to a page");
		}

		wait(Config.WEBDRIVER_WAIT / 2);
		updateSite(getCurrentUrl());
	}

	/**
	 * Gets web element
	 * @param p_webElement
	 *            Web element locator
	 * @return Web elements list
	 */
	public WebElement getWebElementFromWebElement(WebElement p_webElement, By p_locator, boolean p_isThrowable)
	{
		pilot.log().debug("Get web element from web element => " + p_webElement + " locator: " + p_locator);

		try
		{
			WebElement webElement = p_webElement.findElement(p_locator);
			doBeforeActions(webElement);

			return webElement;
		}
		catch (Exception p_exception)
		{
			if (p_isThrowable)
			{
				throw new TestException("Cannot get web element " + p_locator + " from web element " + p_webElement + "\n" + p_exception);
			}

			return null;
		}
	}

	public void verifySelectedOptionText(By p_locator, String p_textExpected)
	{
		String textActual = p_textExpected;

		if (!textActual.equals(p_textExpected))
		{
			throw new TestException("Text '" + textActual + "'is not equal to '" + p_textExpected + "'");
		}
	}

	private void updateSite(String p_siteURL)
	{
		String siteUrl = "";

		if (!pilot.dashboard().getSiteUrl().equals(""))
		{
			try
			{
				siteUrl = p_siteURL.substring(0, p_siteURL.indexOf("/", p_siteURL.indexOf("://") + 3));
			}
			catch (StringIndexOutOfBoundsException p_ex)
			{
				siteUrl = p_siteURL.substring(0, p_siteURL.length());
			}
		}
		else
		{
			try
			{
				siteUrl = p_siteURL.substring(0, p_siteURL.indexOf("/", p_siteURL.indexOf("://") + 3));
				// proxy = true;
			}
			catch (StringIndexOutOfBoundsException p_ex)
			{
				siteUrl = p_siteURL.substring(0, p_siteURL.length());
			}
		}

		if (!pilot.dashboard().getSiteUrl().equals(siteUrl))
		{
			pilot.dashboard().setSiteUrl(siteUrl);
		}
	}

	public boolean wait(By locatorOfElement)
	{
		try
		{
			waitForVisibility(locatorOfElement, null, true);
			return true;
		}
		catch (Exception p_ex)
		{
			pilot.log().warn(p_ex.toString());
			return false;
		}
	}

	private void doBeforeActions(WebElement p_webElement)
	{
		if (pilot.config().WEBDRIVER_FLASH_ELEMENTS)
		{
			flashWebElement(p_webElement);
		}
	}

}
